/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2013 Erwin Coumans  http://bulletphysics.org

This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

///b3DynamicBvhBroadphase implementation by Nathanael Presson

#include "b3DynamicBvhBroadphase.h"
#include "b3OverlappingPair.h"

//
// Profiling
//

#if B3_DBVT_BP_PROFILE||B3_DBVT_BP_ENABLE_BENCHMARK
#include <stdio.h>
#endif

#if B3_DBVT_BP_PROFILE
struct	b3ProfileScope
{
	__forceinline b3ProfileScope(b3Clock& clock,unsigned long& value) :
	m_clock(&clock),m_value(&value),m_base(clock.getTimeMicroseconds())
	{
	}
	__forceinline ~b3ProfileScope()
	{
		(*m_value)+=m_clock->getTimeMicroseconds()-m_base;
	}
	b3Clock*		m_clock;
	unsigned long*	m_value;
	unsigned long	m_base;
};
#define	b3SPC(_value_)	b3ProfileScope	spc_scope(m_clock,_value_)
#else
#define	b3SPC(_value_)
#endif

//
// Helpers
//

//
template <typename T>
static inline void	b3ListAppend(T* item,T*& list)
{
	item->links[0]=0;
	item->links[1]=list;
	if(list) list->links[0]=item;
	list=item;
}

//
template <typename T>
static inline void	b3ListRemove(T* item,T*& list)
{
	if(item->links[0]) item->links[0]->links[1]=item->links[1]; else list=item->links[1];
	if(item->links[1]) item->links[1]->links[0]=item->links[0];
}

//
template <typename T>
static inline int	b3ListCount(T* root)
{
	int	n=0;
	while(root) { ++n;root=root->links[1]; }
	return(n);
}

//
template <typename T>
static inline void	b3Clear(T& value)
{
	static const struct ZeroDummy : T {} zerodummy;
	value=zerodummy;
}

//
// Colliders
//

/* Tree collider	*/ 
struct	b3DbvtTreeCollider : b3DynamicBvh::ICollide
{
	b3DynamicBvhBroadphase*	pbp;
	b3DbvtProxy*		proxy;
	b3DbvtTreeCollider(b3DynamicBvhBroadphase* p) : pbp(p) {}
	void	Process(const b3DbvtNode* na,const b3DbvtNode* nb)
	{
		if(na!=nb)
		{
			b3DbvtProxy*	pa=(b3DbvtProxy*)na->data;
			b3DbvtProxy*	pb=(b3DbvtProxy*)nb->data;
#if B3_DBVT_BP_SORTPAIRS
			if(pa->m_uniqueId>pb->m_uniqueId) 
				b3Swap(pa,pb);
#endif
			pbp->m_paircache->addOverlappingPair(pa->getUid(),pb->getUid());
			++pbp->m_newpairs;
		}
	}
	void	Process(const b3DbvtNode* n)
	{
		Process(n,proxy->leaf);
	}
};

//
// b3DynamicBvhBroadphase
//

//
b3DynamicBvhBroadphase::b3DynamicBvhBroadphase(int proxyCapacity, b3OverlappingPairCache* paircache)
{
	m_deferedcollide	=	false;
	m_needcleanup		=	true;
	m_releasepaircache	=	(paircache!=0)?false:true;
	m_prediction		=	0;
	m_stageCurrent		=	0;
	m_fixedleft			=	0;
	m_fupdates			=	1;
	m_dupdates			=	0;
	m_cupdates			=	10;
	m_newpairs			=	1;
	m_updates_call		=	0;
	m_updates_done		=	0;
	m_updates_ratio		=	0;
	m_paircache			=	paircache? paircache	: new(b3AlignedAlloc(sizeof(b3HashedOverlappingPairCache),16)) b3HashedOverlappingPairCache();
	
	m_pid				=	0;
	m_cid				=	0;
	for(int i=0;i<=STAGECOUNT;++i)
	{
		m_stageRoots[i]=0;
	}
#if B3_DBVT_BP_PROFILE
	b3Clear(m_profiling);
#endif
	m_proxies.resize(proxyCapacity);
}

//
b3DynamicBvhBroadphase::~b3DynamicBvhBroadphase()
{
	if(m_releasepaircache) 
	{
		m_paircache->~b3OverlappingPairCache();
		b3AlignedFree(m_paircache);
	}
}

//
b3BroadphaseProxy*				b3DynamicBvhBroadphase::createProxy(	const b3Vector3& aabbMin,
															  const b3Vector3& aabbMax,
															  int objectId,
															  void* userPtr,
															  short int collisionFilterGroup,
															  short int collisionFilterMask)
{
	b3DbvtProxy* mem = &m_proxies[objectId];
	b3DbvtProxy*		proxy=new(mem) b3DbvtProxy(	aabbMin,aabbMax,userPtr,
		collisionFilterGroup,
		collisionFilterMask);

	b3DbvtAabbMm aabb = b3DbvtVolume::FromMM(aabbMin,aabbMax);

	//bproxy->aabb			=	b3DbvtVolume::FromMM(aabbMin,aabbMax);
	proxy->stage		=	m_stageCurrent;
	proxy->m_uniqueId	=	objectId;
	proxy->leaf			=	m_sets[0].insert(aabb,proxy);
	b3ListAppend(proxy,m_stageRoots[m_stageCurrent]);
	if(!m_deferedcollide)
	{
		b3DbvtTreeCollider	collider(this);
		collider.proxy=proxy;
		m_sets[0].collideTV(m_sets[0].m_root,aabb,collider);
		m_sets[1].collideTV(m_sets[1].m_root,aabb,collider);
	}
	return(proxy);
}

//
void							b3DynamicBvhBroadphase::destroyProxy(	b3BroadphaseProxy* absproxy,
															   b3Dispatcher* dispatcher)
{
	b3DbvtProxy*	proxy=(b3DbvtProxy*)absproxy;
	if(proxy->stage==STAGECOUNT)
		m_sets[1].remove(proxy->leaf);
	else
		m_sets[0].remove(proxy->leaf);
	b3ListRemove(proxy,m_stageRoots[proxy->stage]);
	m_paircache->removeOverlappingPairsContainingProxy(proxy->getUid(),dispatcher);
	
	m_needcleanup=true;
}

void	b3DynamicBvhBroadphase::getAabb(int objectId,b3Vector3& aabbMin, b3Vector3& aabbMax ) const
{
	const b3DbvtProxy*						proxy=&m_proxies[objectId];
	aabbMin = proxy->m_aabbMin;
	aabbMax = proxy->m_aabbMax;
}
/*
void	b3DynamicBvhBroadphase::getAabb(b3BroadphaseProxy* absproxy,b3Vector3& aabbMin, b3Vector3& aabbMax ) const
{
	b3DbvtProxy*						proxy=(b3DbvtProxy*)absproxy;
	aabbMin = proxy->m_aabbMin;
	aabbMax = proxy->m_aabbMax;
}
*/


struct	BroadphaseRayTester : b3DynamicBvh::ICollide
{
	b3BroadphaseRayCallback& m_rayCallback;
	BroadphaseRayTester(b3BroadphaseRayCallback& orgCallback)
		:m_rayCallback(orgCallback)
	{
	}
	void					Process(const b3DbvtNode* leaf)
	{
		b3DbvtProxy*	proxy=(b3DbvtProxy*)leaf->data;
		m_rayCallback.process(proxy);
	}
};	

void	b3DynamicBvhBroadphase::rayTest(const b3Vector3& rayFrom,const b3Vector3& rayTo, b3BroadphaseRayCallback& rayCallback,const b3Vector3& aabbMin,const b3Vector3& aabbMax)
{
	BroadphaseRayTester callback(rayCallback);

	m_sets[0].rayTestInternal(	m_sets[0].m_root,
		rayFrom,
		rayTo,
		rayCallback.m_rayDirectionInverse,
		rayCallback.m_signs,
		rayCallback.m_lambda_max,
		aabbMin,
		aabbMax,
		callback);

	m_sets[1].rayTestInternal(	m_sets[1].m_root,
		rayFrom,
		rayTo,
		rayCallback.m_rayDirectionInverse,
		rayCallback.m_signs,
		rayCallback.m_lambda_max,
		aabbMin,
		aabbMax,
		callback);

}


struct	BroadphaseAabbTester : b3DynamicBvh::ICollide
{
	b3BroadphaseAabbCallback& m_aabbCallback;
	BroadphaseAabbTester(b3BroadphaseAabbCallback& orgCallback)
		:m_aabbCallback(orgCallback)
	{
	}
	void					Process(const b3DbvtNode* leaf)
	{
		b3DbvtProxy*	proxy=(b3DbvtProxy*)leaf->data;
		m_aabbCallback.process(proxy);
	}
};	

void	b3DynamicBvhBroadphase::aabbTest(const b3Vector3& aabbMin,const b3Vector3& aabbMax,b3BroadphaseAabbCallback& aabbCallback)
{
	BroadphaseAabbTester callback(aabbCallback);

	const B3_ATTRIBUTE_ALIGNED16(b3DbvtVolume)	bounds=b3DbvtVolume::FromMM(aabbMin,aabbMax);
		//process all children, that overlap with  the given AABB bounds
	m_sets[0].collideTV(m_sets[0].m_root,bounds,callback);
	m_sets[1].collideTV(m_sets[1].m_root,bounds,callback);

}



//
void							b3DynamicBvhBroadphase::setAabb(int objectId,
														  const b3Vector3& aabbMin,
														  const b3Vector3& aabbMax,
														  b3Dispatcher* /*dispatcher*/)
{
	b3DbvtProxy*						proxy=&m_proxies[objectId];
//	b3DbvtProxy*						proxy=(b3DbvtProxy*)absproxy;
	B3_ATTRIBUTE_ALIGNED16(b3DbvtVolume)	aabb=b3DbvtVolume::FromMM(aabbMin,aabbMax);
#if B3_DBVT_BP_PREVENTFALSEUPDATE
	if(b3NotEqual(aabb,proxy->leaf->volume))
#endif
	{
		bool	docollide=false;
		if(proxy->stage==STAGECOUNT)
		{/* fixed -> dynamic set	*/ 
			m_sets[1].remove(proxy->leaf);
			proxy->leaf=m_sets[0].insert(aabb,proxy);
			docollide=true;
		}
		else
		{/* dynamic set				*/ 
			++m_updates_call;
			if(b3Intersect(proxy->leaf->volume,aabb))
			{/* Moving				*/ 

				const b3Vector3	delta=aabbMin-proxy->m_aabbMin;
				b3Vector3		velocity(((proxy->m_aabbMax-proxy->m_aabbMin)/2)*m_prediction);
				if(delta[0]<0) velocity[0]=-velocity[0];
				if(delta[1]<0) velocity[1]=-velocity[1];
				if(delta[2]<0) velocity[2]=-velocity[2];
				if	(
#ifdef B3_DBVT_BP_MARGIN				
					m_sets[0].update(proxy->leaf,aabb,velocity,B3_DBVT_BP_MARGIN)
#else
					m_sets[0].update(proxy->leaf,aabb,velocity)
#endif
					)
				{
					++m_updates_done;
					docollide=true;
				}
			}
			else
			{/* Teleporting			*/ 
				m_sets[0].update(proxy->leaf,aabb);
				++m_updates_done;
				docollide=true;
			}	
		}
		b3ListRemove(proxy,m_stageRoots[proxy->stage]);
		proxy->m_aabbMin = aabbMin;
		proxy->m_aabbMax = aabbMax;
		proxy->stage	=	m_stageCurrent;
		b3ListAppend(proxy,m_stageRoots[m_stageCurrent]);
		if(docollide)
		{
			m_needcleanup=true;
			if(!m_deferedcollide)
			{
				b3DbvtTreeCollider	collider(this);
				m_sets[1].collideTTpersistentStack(m_sets[1].m_root,proxy->leaf,collider);
				m_sets[0].collideTTpersistentStack(m_sets[0].m_root,proxy->leaf,collider);
			}
		}	
	}
}


//
void							b3DynamicBvhBroadphase::setAabbForceUpdate(		b3BroadphaseProxy* absproxy,
														  const b3Vector3& aabbMin,
														  const b3Vector3& aabbMax,
														  b3Dispatcher* /*dispatcher*/)
{
	b3DbvtProxy*						proxy=(b3DbvtProxy*)absproxy;
	B3_ATTRIBUTE_ALIGNED16(b3DbvtVolume)	aabb=b3DbvtVolume::FromMM(aabbMin,aabbMax);
	bool	docollide=false;
	if(proxy->stage==STAGECOUNT)
	{/* fixed -> dynamic set	*/ 
		m_sets[1].remove(proxy->leaf);
		proxy->leaf=m_sets[0].insert(aabb,proxy);
		docollide=true;
	}
	else
	{/* dynamic set				*/ 
		++m_updates_call;
		/* Teleporting			*/ 
		m_sets[0].update(proxy->leaf,aabb);
		++m_updates_done;
		docollide=true;
	}
	b3ListRemove(proxy,m_stageRoots[proxy->stage]);
	proxy->m_aabbMin = aabbMin;
	proxy->m_aabbMax = aabbMax;
	proxy->stage	=	m_stageCurrent;
	b3ListAppend(proxy,m_stageRoots[m_stageCurrent]);
	if(docollide)
	{
		m_needcleanup=true;
		if(!m_deferedcollide)
		{
			b3DbvtTreeCollider	collider(this);
			m_sets[1].collideTTpersistentStack(m_sets[1].m_root,proxy->leaf,collider);
			m_sets[0].collideTTpersistentStack(m_sets[0].m_root,proxy->leaf,collider);
		}
	}	
}

//
void							b3DynamicBvhBroadphase::calculateOverlappingPairs(b3Dispatcher* dispatcher)
{
	collide(dispatcher);
#if B3_DBVT_BP_PROFILE
	if(0==(m_pid%B3_DBVT_BP_PROFILING_RATE))
	{	
		printf("fixed(%u) dynamics(%u) pairs(%u)\r\n",m_sets[1].m_leaves,m_sets[0].m_leaves,m_paircache->getNumOverlappingPairs());
		unsigned int	total=m_profiling.m_total;
		if(total<=0) total=1;
		printf("ddcollide: %u%% (%uus)\r\n",(50+m_profiling.m_ddcollide*100)/total,m_profiling.m_ddcollide/B3_DBVT_BP_PROFILING_RATE);
		printf("fdcollide: %u%% (%uus)\r\n",(50+m_profiling.m_fdcollide*100)/total,m_profiling.m_fdcollide/B3_DBVT_BP_PROFILING_RATE);
		printf("cleanup:   %u%% (%uus)\r\n",(50+m_profiling.m_cleanup*100)/total,m_profiling.m_cleanup/B3_DBVT_BP_PROFILING_RATE);
		printf("total:     %uus\r\n",total/B3_DBVT_BP_PROFILING_RATE);
		const unsigned long	sum=m_profiling.m_ddcollide+
			m_profiling.m_fdcollide+
			m_profiling.m_cleanup;
		printf("leaked: %u%% (%uus)\r\n",100-((50+sum*100)/total),(total-sum)/B3_DBVT_BP_PROFILING_RATE);
		printf("job counts: %u%%\r\n",(m_profiling.m_jobcount*100)/((m_sets[0].m_leaves+m_sets[1].m_leaves)*B3_DBVT_BP_PROFILING_RATE));
		b3Clear(m_profiling);
		m_clock.reset();
	}
#endif

	performDeferredRemoval(dispatcher);

}

void b3DynamicBvhBroadphase::performDeferredRemoval(b3Dispatcher* dispatcher)
{

	if (m_paircache->hasDeferredRemoval())
	{

		b3BroadphasePairArray&	overlappingPairArray = m_paircache->getOverlappingPairArray();

		//perform a sort, to find duplicates and to sort 'invalid' pairs to the end
		overlappingPairArray.quickSort(b3BroadphasePairSortPredicate());

		int invalidPair = 0;

		
		int i;

		b3BroadphasePair previousPair = b3MakeBroadphasePair(-1,-1);
		
		
		
		for (i=0;i<overlappingPairArray.size();i++)
		{
		
			b3BroadphasePair& pair = overlappingPairArray[i];

			bool isDuplicate = (pair == previousPair);

			previousPair = pair;

			bool needsRemoval = false;

			if (!isDuplicate)
			{
				//important to perform AABB check that is consistent with the broadphase
				b3DbvtProxy*		pa=&m_proxies[pair.x];
				b3DbvtProxy*		pb=&m_proxies[pair.y];
				bool hasOverlap = b3Intersect(pa->leaf->volume,pb->leaf->volume);

				if (hasOverlap)
				{
					needsRemoval = false;
				} else
				{
					needsRemoval = true;
				}
			} else
			{
				//remove duplicate
				needsRemoval = true;
				//should have no algorithm
			}
			
			if (needsRemoval)
			{
				m_paircache->cleanOverlappingPair(pair,dispatcher);

				pair.x = -1;
				pair.y = -1;
				invalidPair++;
			} 
			
		}

		//perform a sort, to sort 'invalid' pairs to the end
		overlappingPairArray.quickSort(b3BroadphasePairSortPredicate());
		overlappingPairArray.resize(overlappingPairArray.size() - invalidPair);
	}
}

//
void							b3DynamicBvhBroadphase::collide(b3Dispatcher* dispatcher)
{
	/*printf("---------------------------------------------------------\n");
	printf("m_sets[0].m_leaves=%d\n",m_sets[0].m_leaves);
	printf("m_sets[1].m_leaves=%d\n",m_sets[1].m_leaves);
	printf("numPairs = %d\n",getOverlappingPairCache()->getNumOverlappingPairs());
	{
		int i;
		for (i=0;i<getOverlappingPairCache()->getNumOverlappingPairs();i++)
		{
			printf("pair[%d]=(%d,%d),",i,getOverlappingPairCache()->getOverlappingPairArray()[i].m_pProxy0->getUid(),
				getOverlappingPairCache()->getOverlappingPairArray()[i].m_pProxy1->getUid());
		}
		printf("\n");
	}
*/



	b3SPC(m_profiling.m_total);
	/* optimize				*/ 
	m_sets[0].optimizeIncremental(1+(m_sets[0].m_leaves*m_dupdates)/100);
	if(m_fixedleft)
	{
		const int count=1+(m_sets[1].m_leaves*m_fupdates)/100;
		m_sets[1].optimizeIncremental(1+(m_sets[1].m_leaves*m_fupdates)/100);
		m_fixedleft=b3Max<int>(0,m_fixedleft-count);
	}
	/* dynamic -> fixed set	*/ 
	m_stageCurrent=(m_stageCurrent+1)%STAGECOUNT;
	b3DbvtProxy*	current=m_stageRoots[m_stageCurrent];
	if(current)
	{
		b3DbvtTreeCollider	collider(this);
		do	{
			b3DbvtProxy*	next=current->links[1];
			b3ListRemove(current,m_stageRoots[current->stage]);
			b3ListAppend(current,m_stageRoots[STAGECOUNT]);
#if B3_DBVT_BP_ACCURATESLEEPING
			m_paircache->removeOverlappingPairsContainingProxy(current,dispatcher);
			collider.proxy=current;
			b3DynamicBvh::collideTV(m_sets[0].m_root,current->aabb,collider);
			b3DynamicBvh::collideTV(m_sets[1].m_root,current->aabb,collider);
#endif
			m_sets[0].remove(current->leaf);
			B3_ATTRIBUTE_ALIGNED16(b3DbvtVolume)	curAabb=b3DbvtVolume::FromMM(current->m_aabbMin,current->m_aabbMax);
			current->leaf	=	m_sets[1].insert(curAabb,current);
			current->stage	=	STAGECOUNT;	
			current			=	next;
		} while(current);
		m_fixedleft=m_sets[1].m_leaves;
		m_needcleanup=true;
	}
	/* collide dynamics		*/ 
	{
		b3DbvtTreeCollider	collider(this);
		if(m_deferedcollide)
		{
			b3SPC(m_profiling.m_fdcollide);
			m_sets[0].collideTTpersistentStack(m_sets[0].m_root,m_sets[1].m_root,collider);
		}
		if(m_deferedcollide)
		{
			b3SPC(m_profiling.m_ddcollide);
			m_sets[0].collideTTpersistentStack(m_sets[0].m_root,m_sets[0].m_root,collider);
		}
	}
	/* clean up				*/ 
	if(m_needcleanup)
	{
		b3SPC(m_profiling.m_cleanup);
		b3BroadphasePairArray&	pairs=m_paircache->getOverlappingPairArray();
		if(pairs.size()>0)
		{

			int			ni=b3Min(pairs.size(),b3Max<int>(m_newpairs,(pairs.size()*m_cupdates)/100));
			for(int i=0;i<ni;++i)
			{
				b3BroadphasePair&	p=pairs[(m_cid+i)%pairs.size()];
				b3DbvtProxy*		pa=&m_proxies[p.x];
				b3DbvtProxy*		pb=&m_proxies[p.y];
				if(!b3Intersect(pa->leaf->volume,pb->leaf->volume))
				{
#if B3_DBVT_BP_SORTPAIRS
					if(pa->m_uniqueId>pb->m_uniqueId) 
						b3Swap(pa,pb);
#endif
					m_paircache->removeOverlappingPair(pa->getUid(),pb->getUid(),dispatcher);
					--ni;--i;
				}
			}
			if(pairs.size()>0) m_cid=(m_cid+ni)%pairs.size(); else m_cid=0;
		}
	}
	++m_pid;
	m_newpairs=1;
	m_needcleanup=false;
	if(m_updates_call>0)
	{ m_updates_ratio=m_updates_done/(b3Scalar)m_updates_call; }
	else
	{ m_updates_ratio=0; }
	m_updates_done/=2;
	m_updates_call/=2;
}

//
void							b3DynamicBvhBroadphase::optimize()
{
	m_sets[0].optimizeTopDown();
	m_sets[1].optimizeTopDown();
}

//
b3OverlappingPairCache*			b3DynamicBvhBroadphase::getOverlappingPairCache()
{
	return(m_paircache);
}

//
const b3OverlappingPairCache*	b3DynamicBvhBroadphase::getOverlappingPairCache() const
{
	return(m_paircache);
}

//
void							b3DynamicBvhBroadphase::getBroadphaseAabb(b3Vector3& aabbMin,b3Vector3& aabbMax) const
{

	B3_ATTRIBUTE_ALIGNED16(b3DbvtVolume)	bounds;

	if(!m_sets[0].empty())
		if(!m_sets[1].empty())	b3Merge(	m_sets[0].m_root->volume,
			m_sets[1].m_root->volume,bounds);
		else
			bounds=m_sets[0].m_root->volume;
	else if(!m_sets[1].empty())	bounds=m_sets[1].m_root->volume;
	else
		bounds=b3DbvtVolume::FromCR(b3MakeVector3(0,0,0),0);
	aabbMin=bounds.Mins();
	aabbMax=bounds.Maxs();
}

void b3DynamicBvhBroadphase::resetPool(b3Dispatcher* dispatcher)
{
	
	int totalObjects = m_sets[0].m_leaves + m_sets[1].m_leaves;
	if (!totalObjects)
	{
		//reset internal dynamic tree data structures
		m_sets[0].clear();
		m_sets[1].clear();
		
		m_deferedcollide	=	false;
		m_needcleanup		=	true;
		m_stageCurrent		=	0;
		m_fixedleft			=	0;
		m_fupdates			=	1;
		m_dupdates			=	0;
		m_cupdates			=	10;
		m_newpairs			=	1;
		m_updates_call		=	0;
		m_updates_done		=	0;
		m_updates_ratio		=	0;
		
		m_pid				=	0;
		m_cid				=	0;
		for(int i=0;i<=STAGECOUNT;++i)
		{
			m_stageRoots[i]=0;
		}
	}
}

//
void							b3DynamicBvhBroadphase::printStats()
{}

//
#if B3_DBVT_BP_ENABLE_BENCHMARK

struct	b3BroadphaseBenchmark
{
	struct	Experiment
	{
		const char*			name;
		int					object_count;
		int					update_count;
		int					spawn_count;
		int					iterations;
		b3Scalar			speed;
		b3Scalar			amplitude;
	};
	struct	Object
	{
		b3Vector3			center;
		b3Vector3			extents;
		b3BroadphaseProxy*	proxy;
		b3Scalar			time;
		void				update(b3Scalar speed,b3Scalar amplitude,b3BroadphaseInterface* pbi)
		{
			time		+=	speed;
			center[0]	=	b3Cos(time*(b3Scalar)2.17)*amplitude+
				b3Sin(time)*amplitude/2;
			center[1]	=	b3Cos(time*(b3Scalar)1.38)*amplitude+
				b3Sin(time)*amplitude;
			center[2]	=	b3Sin(time*(b3Scalar)0.777)*amplitude;
			pbi->setAabb(proxy,center-extents,center+extents,0);
		}
	};
	static int		UnsignedRand(int range=RAND_MAX-1)	{ return(rand()%(range+1)); }
	static b3Scalar	UnitRand()							{ return(UnsignedRand(16384)/(b3Scalar)16384); }
	static void		OutputTime(const char* name,b3Clock& c,unsigned count=0)
	{
		const unsigned long	us=c.getTimeMicroseconds();
		const unsigned long	ms=(us+500)/1000;
		const b3Scalar		sec=us/(b3Scalar)(1000*1000);
		if(count>0)
			printf("%s : %u us (%u ms), %.2f/s\r\n",name,us,ms,count/sec);
		else
			printf("%s : %u us (%u ms)\r\n",name,us,ms);
	}
};

void							b3DynamicBvhBroadphase::benchmark(b3BroadphaseInterface* pbi)
{
	static const b3BroadphaseBenchmark::Experiment		experiments[]=
	{
		{"1024o.10%",1024,10,0,8192,(b3Scalar)0.005,(b3Scalar)100},
		/*{"4096o.10%",4096,10,0,8192,(b3Scalar)0.005,(b3Scalar)100},
		{"8192o.10%",8192,10,0,8192,(b3Scalar)0.005,(b3Scalar)100},*/
	};
	static const int										nexperiments=sizeof(experiments)/sizeof(experiments[0]);
	b3AlignedObjectArray<b3BroadphaseBenchmark::Object*>	objects;
	b3Clock													wallclock;
	/* Begin			*/ 
	for(int iexp=0;iexp<nexperiments;++iexp)
	{
		const b3BroadphaseBenchmark::Experiment&	experiment=experiments[iexp];
		const int									object_count=experiment.object_count;
		const int									update_count=(object_count*experiment.update_count)/100;
		const int									spawn_count=(object_count*experiment.spawn_count)/100;
		const b3Scalar								speed=experiment.speed;	
		const b3Scalar								amplitude=experiment.amplitude;
		printf("Experiment #%u '%s':\r\n",iexp,experiment.name);
		printf("\tObjects: %u\r\n",object_count);
		printf("\tUpdate: %u\r\n",update_count);
		printf("\tSpawn: %u\r\n",spawn_count);
		printf("\tSpeed: %f\r\n",speed);
		printf("\tAmplitude: %f\r\n",amplitude);
		srand(180673);
		/* Create objects	*/ 
		wallclock.reset();
		objects.reserve(object_count);
		for(int i=0;i<object_count;++i)
		{
			b3BroadphaseBenchmark::Object*	po=new b3BroadphaseBenchmark::Object();
			po->center[0]=b3BroadphaseBenchmark::UnitRand()*50;
			po->center[1]=b3BroadphaseBenchmark::UnitRand()*50;
			po->center[2]=b3BroadphaseBenchmark::UnitRand()*50;
			po->extents[0]=b3BroadphaseBenchmark::UnitRand()*2+2;
			po->extents[1]=b3BroadphaseBenchmark::UnitRand()*2+2;
			po->extents[2]=b3BroadphaseBenchmark::UnitRand()*2+2;
			po->time=b3BroadphaseBenchmark::UnitRand()*2000;
			po->proxy=pbi->createProxy(po->center-po->extents,po->center+po->extents,0,po,1,1,0,0);
			objects.push_back(po);
		}
		b3BroadphaseBenchmark::OutputTime("\tInitialization",wallclock);
		/* First update		*/ 
		wallclock.reset();
		for(int i=0;i<objects.size();++i)
		{
			objects[i]->update(speed,amplitude,pbi);
		}
		b3BroadphaseBenchmark::OutputTime("\tFirst update",wallclock);
		/* Updates			*/ 
		wallclock.reset();
		for(int i=0;i<experiment.iterations;++i)
		{
			for(int j=0;j<update_count;++j)
			{				
				objects[j]->update(speed,amplitude,pbi);
			}
			pbi->calculateOverlappingPairs(0);
		}
		b3BroadphaseBenchmark::OutputTime("\tUpdate",wallclock,experiment.iterations);
		/* Clean up			*/ 
		wallclock.reset();
		for(int i=0;i<objects.size();++i)
		{
			pbi->destroyProxy(objects[i]->proxy,0);
			delete objects[i];
		}
		objects.resize(0);
		b3BroadphaseBenchmark::OutputTime("\tRelease",wallclock);
	}

}
#else
/*void							b3DynamicBvhBroadphase::benchmark(b3BroadphaseInterface*)
{}
*/
#endif

#if B3_DBVT_BP_PROFILE
#undef	b3SPC
#endif

